/*
 * Copyright (c) 2009 Robert Esser
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package net.e2ser.component;

import java.io.IOException;
import java.util.ArrayList;

import net.e2ser.petri.Color;
import net.e2ser.petri.Editor;
import net.e2ser.petri.RobPoint;
import net.e2ser.petri.SimulatorMode;
import net.e2ser.token.Token;
import net.e2ser.token.TokenStore;
import net.e2ser.util.XMLWriter;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.graphics.Paint.Align;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.shapes.OvalShape;

/**
 * 
 * <p>
 * Title: Petri Net Editor/Simulator
 * </p>
 * <p>
 * Description: Draws and maintains place information.
 * </p>
 * <p>
 * Copyright: Copyright (c) 2009
 * </p>
 * <p>
 * Company:
 * </p>
 * 
 * @author Rob Esser
 * @version 1.0
 */
public class Place extends AbstractComponent {

  /**
   * where the current tokens are kept Note that we are storing explicit tokens
   * where we could just store an integer to represent the number of tokens. One
   * day tokens may have values....
   */
  TokenStore tokens = new TokenStore();

  public Place(RobComponent parent, double scale) {
    this(parent, 0, 0, scale);
  }

  public Place(RobComponent parent, int x, int y, double scale) {
    super(parent, x, y);
    shape = new ShapeDrawable(new OvalShape());
    Rect r = new Rect(getComponentBounds());
    r.right = r.left + (int) (AbstractComponent.DefaultComponentWidth() * scale);
    r.bottom = r.top + (int) (AbstractComponent.DefaultComponentHeight() * scale);
    shape.setBounds(r);
    fillColor = Color.ORANGE;
    Paint paint = shape.getPaint();
    paint.setColor(fillColor);
    paint.setTextAlign(Align.CENTER);
    recalulateTextSize(); 
    paint.setTextSize(textSize());
    paint.setTypeface(Typeface.SANS_SERIF);
  }

  public Place(RobComponent parent, String name, int x, int y, int width, int height, double scale) {
    super(parent, x, y, width, height);
    shape = new ShapeDrawable(new OvalShape());
    // int left, int top, int right, int bottom
    shape.setBounds(x, y, x + width, y + height);
    resize(scale);
    this.name = name;
    fillColor = Color.ORANGE;
    Paint paint = shape.getPaint();
    paint.setColor(fillColor);
    paint.setTextAlign(Align.CENTER);
    paint.setTextSize(textSize());
    paint.setTypeface(Typeface.SANS_SERIF);
  }


  /**
   * return the current number of tokens
   * 
   * @return int
   */
  public int tokensSize() {
    return tokens.size();
  }

  /**
   * add a token into the token store
   * 
   * @param animation
   *          boolean
   */
  public void addToken(boolean animation) {
    tokens.addToken(new Token());
    if (animation)
      parent.changed(this);
  }

  /**
   * add a token into the token store
   */
  public void addToken() {
    this.addToken(true);
  }

  /**
   * remove a token from the token store
   * 
   * @param animation
   *          boolean
   */
  public void removeToken(boolean animation) {
    tokens.removeToken();
    if (animation)
      parent.changed(this);
  }

  /**
   * remove a token from the token store
   */
  public void removeToken() {
    this.removeToken(true);
  }

  /**
   * I am never fireable so do nothing
   * 
   * @param fireList List
   * @param mode SimulatorMode
   */
  public void addFireable(ArrayList<AbstractComponent> fireList, SimulatorMode mode) {
  }

  /**
   * insert a new Place in parent
   * 
   * @param parent
   *          RobComponent
   * @param x
   *          int
   * @param y
   *          int
   * @return AbstractComponent
   */
  public AbstractComponent insertElementIn(RobComponent parent, int x, int y, double scale) {
    AbstractComponent n = new Place(parent, x, y, scale);
    parent.insertElementAt(n, x, y);
    return n;
  }

  /**
   * Assumes an oval perimeter
   * 
   * @param otherEnd RobPoint
   * @return RobPoint
   */
  RobPoint connectionPoint(RobPoint otherEndCenter) {
    RobPoint myCenter = center();
    double angle = Math.atan2(otherEndCenter.y - myCenter.y, otherEndCenter.x - myCenter.x);
    double halfcos = Math.cos(angle) / 2;
    double halfsin = Math.sin(angle) / 2;
    return new RobPoint((int) Math.round(halfcos * (bounds().width() + 1)) + myCenter.x, (int) Math.round(halfsin * (bounds().height() + 1)) + myCenter.y);
  }
  
  /**
   * draw myself on the Canvas
   * 
   * @param canvas
   *          where to draw
   */
  public void draw(Canvas canvas) {
    privateDraw(canvas, false);
  }
  
  private void privateDraw(Canvas canvas, boolean selected) {
    Rect origBounds = new Rect(shape.getBounds());
    Rect fillBounds = new Rect(origBounds);
    if (selected) {
      fillBounds.inset(3, 3);
      // draw border
      shape.getPaint().setColor(Color.darker(Color.darker(fillColor)));
    } else {
      fillBounds.inset(1, 1);
      // draw border
      shape.getPaint().setColor(Color.BLACK);
    }
    shape.draw(canvas);
    // draw body
    shape.setBounds(fillBounds);
    if (inverse) {
      shape.getPaint().setColor(Color.GRAY);
    } else {
      shape.getPaint().setColor(fillColor);
    }
    shape.draw(canvas);

    String s = (name == null) ? "place" : name;
    if (tokensSize() == 0) {
      // now the inscription text
      if (s.length() > 0) {
        shape.setBounds(origBounds);
        shape.getPaint().setColor(Color.GRAY);
        canvas.drawText(s, origBounds.centerX() + Editor.TextOffsetX, origBounds.centerY() + Editor.TextOffsetY, shape.getPaint());
      }
    } else {
      // token blob
      shape.getPaint().setColor(Color.BLACK);
      fillBounds = new Rect(origBounds);
      fillBounds.inset(origBounds.width() / 4, origBounds.height() / 4);
      shape.setBounds(fillBounds);
      shape.draw(canvas);
      s = "" + tokensSize();
      // now the token text
      if (s.length() > 0) {
        shape.getPaint().setColor(Color.WHITE);
        canvas.drawText(s, fillBounds.centerX() + Editor.TextOffsetX, fillBounds.centerY() + Editor.TextOffsetY, shape.getPaint());
      }
    }
    shape.setBounds(origBounds);
  }
  
  /**
   * Draw a marker to indicate that the node is selected
   * @param canvas where to draw
   */
  public void drawSelected(Canvas canvas) {
    privateDraw(canvas, true);
  }

  /**
   * Output an XML representation of myself
   * @param writer object that represents writer state
   * @throws IOException 
   */
  public void toXML(XMLWriter writer) throws IOException {
    String id = writer.addElement(this);
    Rect r = shape.getBounds();
    writer.writeLine("<place id=\"" + id + "\" l=\"" + r.left + 
        "\" t=\"" + r.top + "\" r=\"" + r.right + 
        "\" b=\"" + r.bottom + (tokensSize() == 0 ? "" : "\" tok=\"" + tokensSize()) + 
        (name == null ? "" : "\" n=\"" + name) + "\"/>");
  }

}
